package org.dh2580.dev.tools.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import okhttp3.ConnectionPool;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * @Author: hao.deng
 * @Date: 2019/3/25-下午2:41
 */
public class OkHttpUtil {
    private static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);

    private OkHttpUtil() {
    }

    /**
     * Get请求
     *
     * @param url     请求url
     * @param headers 请求头
     * @return 返回请求结果
     */
    public static HttpResult get(String url, Map<String, String> headers) {
        final Request.Builder requestBuilder = new Request.Builder().url(url);
        String userAgent = UserAgentInterceptor.USER_AGENT;

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                requestBuilder.addHeader(header.getKey(), header.getValue());
            }

            userAgent = headers.getOrDefault("User-Agent", userAgent);
        }


        try {
            final Response response = createOkHttpClient(userAgent).newCall(requestBuilder.build()).execute();

            return new HttpResult(response.code(), response.body().string());
        } catch (Exception e) {
            return new HttpResult(e);
        }
    }

    /**
     * Post请求
     *
     * @param url         请求url
     * @param headers     请求头
     * @param requestBody 请求body
     * @return 返回请求结果
     */
    public static HttpResult post(String url, Map<String, String> headers, String requestBody) {
        final Request.Builder requestBuilder = new Request.Builder().url(url);
        String userAgent = UserAgentInterceptor.USER_AGENT;

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                requestBuilder.addHeader(header.getKey(), header.getValue());
            }

            userAgent = headers.getOrDefault("User-Agent", userAgent);
        }

        requestBuilder.post(RequestBody.create(MediaType.parse("application/json;charset=utf-8"), requestBody));

        try {
            final Response response = createOkHttpClient(userAgent).newCall(requestBuilder.build()).execute();

            return new HttpResult(response.code(), response.body().string());
        } catch (Exception e) {
            return new HttpResult(e);
        }
    }

    /**
     * 以form表单形式提交post类型请求
     *
     * @param url     请求url
     * @param headers 请求头
     * @param params  请求的参数
     * @return 返回请求结果
     */
    public static HttpResult postAsForm(String url, Map<String, String> headers, Map<String, Object> params) {
        final Request.Builder requestBuilder = new Request.Builder().url(url);
        String userAgent = UserAgentInterceptor.USER_AGENT;

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                requestBuilder.addHeader(header.getKey(), header.getValue());
            }

            userAgent = headers.getOrDefault("User-Agent", userAgent);
        }

        List<String> requestParams = params.entrySet().stream().filter(g -> g.getValue() != null && g.getKey() != null).map(g -> g.getKey() + "=" + g.getValue().toString()).collect(Collectors.toList());
        String requestBody = StringUtil.join(requestParams, "&");

        requestBuilder.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8"), requestBody));

        try {
            final Response response = createOkHttpClient(userAgent).newCall(requestBuilder.build()).execute();

            return new HttpResult(response.code(), response.body().string());
        } catch (Exception e) {
            return new HttpResult(e);
        }
    }

    /**
     * 下载文件
     *
     * @param url           下载文件url
     * @param headers       请求头
     * @param localFilePath 本地文件路径
     * @return 返回下载的文件
     */
    public static HttpResult download(String url, Map<String, String> headers, String localFilePath) {
        final Request.Builder requestBuilder = new Request.Builder().url(url);
        String userAgent = UserAgentInterceptor.USER_AGENT;

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                requestBuilder.addHeader(header.getKey(), header.getValue());
            }

            userAgent = headers.getOrDefault("User-Agent", userAgent);
        }

        BufferedInputStream input = null;
        FileOutputStream fos = null;

        try {
            final Response response = createOkHttpClient(userAgent).newCall(requestBuilder.build()).execute();

            input = new BufferedInputStream(response.body().byteStream());

            fos = new FileOutputStream(new File(localFilePath));
            byte[] buf = new byte[8092];

            int len = 0;
            while ((len = input.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }

            return new HttpResult(response.code(), localFilePath);
        } catch (Exception e) {
            return new HttpResult(e);
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                logger.error("download error...", e);
            }
        }
    }

    /**
     * 上传文件到服务器
     *
     * @param url           上传url
     * @param headers       请求头
     * @param fileName      上传上去后的文件名称
     * @param localFilePath 本地文件路径
     * @return
     */
    public static HttpResult upload(String url, Map<String, String> headers, String fileName, String localFilePath) {
        final Request.Builder requestBuilder = new Request.Builder().url(url);
        String userAgent = UserAgentInterceptor.USER_AGENT;

        if (headers != null) {
            for (Map.Entry<String, String> header : headers.entrySet()) {
                requestBuilder.addHeader(header.getKey(), header.getValue());
            }

            userAgent = headers.getOrDefault("User-Agent", userAgent);
        }

        try {
            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", fileName, RequestBody.create(MediaType.parse("application/octet-stream"), new File(localFilePath)))
                    .build();

            Request request = requestBuilder.post(requestBody).build();

            Response response = createOkHttpClient(userAgent)
                    .newCall(request)
                    .execute();

            return new HttpResult(response.code(), response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
            return new HttpResult(e);
        }
    }

    private static OkHttpClient createOkHttpClient(String userAgent) {
        return new OkHttpClient.Builder()
                .connectTimeout(3, TimeUnit.SECONDS)
                .readTimeout(600, TimeUnit.SECONDS)
                .writeTimeout(600, TimeUnit.SECONDS)
                .sslSocketFactory(createSSLSocketFactory(), new TrustAllManager())
                .hostnameVerifier(new TrustAllHostnameVerifier())
                .addNetworkInterceptor(new UserAgentInterceptor(userAgent))
                .connectionPool(new ConnectionPool(10, 1, TimeUnit.SECONDS))
                .build();
    }

    /**
     * 处理Https请求
     */
    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory sSLSocketFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllManager()},
                    new SecureRandom());
            sSLSocketFactory = sc.getSocketFactory();
        } catch (Exception e) {
        }
        return sSLSocketFactory;
    }

    //---------- Inner Class

    public static class HttpResult {
        private int code;
        private String content;
        private Throwable throwable;

        public HttpResult(int code, String content) {
            this.code = code;
            this.content = content;
            throwable = null;
        }

        public HttpResult(Throwable throwable) {
            this.code = -1;
            this.content = "";
            this.throwable = throwable;
        }

        public int getCode() {
            return code;
        }

        public String getContent() {
            return content;
        }

        public Throwable getThrowable() {
            return throwable;
        }
    }


    /**
     * 替换用户代理的网络拦截器
     */
    private static class UserAgentInterceptor implements Interceptor {

        public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Mobile Safari/537.36";

        private String userAgent;

        public UserAgentInterceptor(String userAgent) {
            this.userAgent = userAgent;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            request = request.newBuilder()
                    .removeHeader("User-Agent")
                    .addHeader("User-Agent", Optional.ofNullable(userAgent).orElse(USER_AGENT))
                    .build();

            return chain.proceed(request);
        }
    }


    /**
     * 信任所有的证书
     */
    private static class TrustAllManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    private static class TrustAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
}
